﻿/********************************************************************
	author:		tianchunhua
	created:	2010/12/17	
	purpose:	网络数据传输类,开启线程将队列中的数据发送至LanTransPort层，
                接收数据时，将按照公共网络通讯模块的协议方式进行解析
 *             1、采用socket方式
 *             2、增加网络断线检测（有心跳包作用的数据接收程序）
 *             3、接收数据转发至上层，解开耦合性能
*********************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Runtime.InteropServices;

namespace Skyray.Net
{
    internal class LanInterface
    {
        /// <summary>
        /// Log4Net日志
        /// </summary>
        private readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        /// <summary>
        /// 一次接收数据的最大长度
        /// </summary>
        private readonly int ReceivedMaxLength = 1024;
        /// <summary>
        /// 超时时间
        /// </summary>
        private readonly int TimeOut = 200;
        /// <summary>
        /// 创建客户端套接字
        /// </summary>
        private Socket _tcpsocket;
        /// <summary>
        /// 接受数据线程
        /// </summary>
        private Thread _thrReceived;

        /// <summary>
        /// 发送线程
        /// </summary>
        private Thread _thrSend;

        /// <summary>
        /// 网口关闭标记
        /// </summary>
        bool _Exit;

        /// <summary>
        ///  发送队列
        /// </summary>
        private Threaded.TQueue<byte[]> _sendQueue = new Threaded.TQueue<byte[]>();

        /// <summary>
        /// 委托类，收到一帧数据时触发
        /// </summary>
        /// <param name="buf"></param>
        public delegate void ReceiveCallback(Byte[] buf);
        /// <summary>
        /// 委托对象，收到一帧数据时触发
        /// </summary>
        public ReceiveCallback DataReceiveCallBack;

        /// <summary>
        /// 网络无法连接或者断线事件
        /// </summary>
        public LanstatusChangedEventHandler ConnectstatusChangedEvent;
        /// <summary>
        /// Ctor
        /// </summary>
        public LanInterface()
        {
            _tcpsocket = null;
        }
        /// <summary>
        /// 判定是否已经连接
        /// </summary>
        public bool IsStartUp
        {
            get { return _tcpsocket != null /*&& _tcpClient.Connected && !_Exit*/; }
        }
        /// <summary>
        /// 关闭
        /// </summary>
        public void ShutDown()
        {
            _Exit = true;//使线程安全退出  
            if (_thrReceived != null) _thrReceived.Join(5000);
            if (_thrSend != null) _thrSend.Join(5000);
            // 等待发送线程、接收线程结束
            if (_tcpsocket != null)
            {
                _tcpsocket.Close();
                if (ConnectstatusChangedEvent != null)
                {
                    ConnectstatusChangedEvent(LanTransState.Stop);
                }
                _tcpsocket = null;
            }
            _thrReceived = null;
            _thrSend = null;
            _Exit = true;
        }

        /// <summary>
        /// 连接服务器
        /// </summary>
        public bool Connect(string IpAdress, int port)
        {
            ShutDown();
            _tcpsocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //连接到服务器
            try
            {
                uint dummy = 2;
                byte[] inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
                BitConverter.GetBytes((uint)1).CopyTo(inOptionValues, 0);//是否启用Keep-Alive
                BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy));//多长时间开始第一次探测
                BitConverter.GetBytes((uint)5000).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);//探测时间间隔                
                _tcpsocket.Connect(IpAdress, port);
                // _tcpsocket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
                _tcpsocket.ReceiveTimeout = TimeOut;
                if (ConnectstatusChangedEvent != null)
                {
                    ConnectstatusChangedEvent(LanTransState.Normal);
                }
            }
            catch (SocketException ex)
            {
                if (ex.ErrorCode.Equals(10065))
                {
                    Console.WriteLine("无法连接服务器");
                    if (ConnectstatusChangedEvent != null)
                    {
                        ConnectstatusChangedEvent(LanTransState.Refuse);
                    }
                }
                _tcpsocket = null;
                _Exit = true;
                return false;
            }
            //获得与服务器数据交互的流通道
            _Exit = false;

            //开启接收线程
            _thrReceived = new Thread(new ThreadStart(ServerResponse)) { Name = "LanInterface Receive Thread.", IsBackground = true };
            _thrReceived.Start();
            //开启发送线程
            _thrSend = new Thread(new ThreadStart(SendData)) { Name = "LanInterface Send Thread.", IsBackground = true };
            _thrSend.Start();
            return true;
        }

        /// <summary>
        /// 将数据放入发送队列
        /// </summary>
        /// <param name="buf"></param>
        public void Write(Byte[] buf)
        {
            if (buf.Length >= 0 && !_Exit)
            {
                _sendQueue.Enqueue(buf);
            }
        }

        /// <summary>
        /// 接收服务器返回数据
        /// </summary>
        private void ServerResponse()
        {
            _log.Debug(String.Format("exit = {0}", _Exit));
            Byte[] buff = new Byte[ReceivedMaxLength];
            //int recing = 0; //接收到的数据心跳
            //改用socket实现         
            while (!_Exit)
            {
                if (_tcpsocket.Poll(TimeOut, SelectMode.SelectRead))
                {
                    try
                    {
                        int len = _tcpsocket.Receive(buff);
                        if (len > 0)
                        {
                            var rev = new byte[len];
                            Array.ConstrainedCopy(buff, 0, rev, 0, rev.Length);
                            DataReceiveCallBack(rev);
                        }
                    }
                    catch (SocketException socketException)
                    {
                        //Still Connected but the send would block
                        // 10035 == WSAEWOULDBLOCK   
                        if (!socketException.ErrorCode.Equals(10035))
                        {
                            if (ConnectstatusChangedEvent != null)
                            {
                                ConnectstatusChangedEvent(LanTransState.Stop);//检测数据断线情况
                            }
                            _Exit = true;
                        }
                    }
                }
                else
                {
                    Thread.Sleep(10);
                }
            }
            Console.WriteLine("Read Data Exit.");
            return;
        }

        /// <summary>
        /// 发送数据到服务器
        /// </summary>
        /// I:无 P：遍历发送数据队列，给下位机发送数据  O：无
        public void SendData()
        {
            _log.Debug(String.Format("exit = {0}", _Exit));
            while (!_Exit)
            {
                if (_tcpsocket.Poll(TimeOut, SelectMode.SelectWrite))
                {
                    if (_sendQueue.Count > 0)
                    {
                        Byte[] buf = _sendQueue.Dequeue();
                        if (buf != null)
                        {
                            try
                            {
                                _tcpsocket.Send(buf, buf.Length, SocketFlags.None);
                            }
                            catch
                            {
                                _Exit = true;
                            }
                        }
                    }
                    else
                    {
                        Thread.Sleep(50);
                    }
                    //Thread.Sleep(0)  发送数据要求及时性，不做切换；此处将占用很多CPU资源
                }
                else
                {
                    Thread.Sleep(50);
                }
            }
            Console.WriteLine("Send Data Exit.");
        }
    }
}
